package net.siufung.market.base.permit.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNodeConfig;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.siufung.core.exception.BizServiceException;
import net.siufung.core.model.enums.DeletedEnum;
import net.siufung.core.utils.CopyUtil;
import net.siufung.core.utils.StringPool;
import net.siufung.market.base.permit.controller.model.dto.MenuTreeTableParams;
import net.siufung.market.base.permit.controller.model.vo.MenuTreeTableVo;
import net.siufung.market.base.permit.enums.AccessStates;
import net.siufung.market.base.permit.enums.KeepAliveTypes;
import net.siufung.market.base.permit.enums.MenuTypes;
import net.siufung.market.base.permit.mapper.IMenuMapper;
import net.siufung.market.base.permit.mapper.IPermitMapper;
import net.siufung.market.base.permit.mapper.entity.Menu;
import net.siufung.market.base.permit.mapper.entity.Permit;
import net.siufung.market.base.permit.service.IMenuQueryService;
import net.siufung.market.base.permit.spi.IPermitAppMenuSpi;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.TreeSet;
import java.util.stream.Collectors;

/**
 * @author 陈建峰
 * @since 2022/7/11 10:26 下午
 */
@Slf4j
@AllArgsConstructor
@Service(value = "menuQueryService")
@Transactional(readOnly = true, rollbackFor = Exception.class)
public class MenuQueryServiceImpl implements IMenuQueryService {

    private final IMenuMapper menuMapper;
    private final IPermitMapper permitMapper;
    private final IPermitAppMenuSpi permitAppMenuSpi;

    @Override
    public List<Menu> getTreeMenuList(Integer parentId, Boolean hasAccess) {
        if (Objects.isNull(parentId)) {
            throw new BizServiceException("parentId为空！");
        }
        return this.getTreeMenuList(null, parentId, hasAccess);
    }

    @Override
    public List<Menu> getTreeMenuList(String clientId, Integer parentId, Boolean hasAccess) {
        if (Objects.isNull(hasAccess)) {
            throw new BizServiceException("是否包含可执行菜单不能为空！");
        }
        List<Menu> menuList = Lists.newArrayList();
        List<Menu> allMenuList =  menuMapper.selectList(
                new LambdaQueryWrapper<Menu>().eq(Objects.nonNull(clientId), Menu::getAppId, clientId)
                        .orderByAsc(Menu::getId));
        /* 客户端id不为空，则使用app授权范围内的菜单 */
        if(!Strings.isNullOrEmpty(clientId)){
            List<Integer> appMenuIdList = permitAppMenuSpi.getMenuIdListByClientId(clientId);
            allMenuList = getAppMenuList(allMenuList, appMenuIdList);
        }
        if(CollectionUtils.isEmpty(allMenuList)){
            return Lists.newArrayList();
        }
        if (Objects.nonNull(parentId)) {
            Integer finalParentId = parentId;
            if(allMenuList.stream().noneMatch(menu -> finalParentId.equals(menu.getParentId()))){
                throw new BizServiceException("无效的parentId！");
            }
        } else {
            parentId = allMenuList.get(0).getParentId();
        }
        if (parentId == Integer.parseInt(StringPool.ZERO)) {
            menuList = allMenuList.stream()
                    .sorted(Comparator.comparing(Menu::getSequence))
                    .filter(menu -> hasAccess.equals(menu.getAccessState())).collect(Collectors.toList());
        } else {
            menuMapper.getChildMenuList(menuList, parentId, allMenuList);
        }
        return menuList;
    }

    @Override
    public List<Tree<Integer>> getTreeList(Integer parentId, Boolean hasAccess) {
        List<Menu> menuList = getTreeMenuList(parentId, hasAccess);
        List<MenuTreeTableVo> menuTreeTableVoList = Lists.newArrayList();
        MenuTreeTableVo menuTreeTableVo;
        for (Menu menu: menuList) {
            menuTreeTableVo = new MenuTreeTableVo();
            CopyUtil.copyProperties(menu, menuTreeTableVo);
            menuTreeTableVo.setName(menu.getMenuName());
            menuTreeTableVoList.add(menuTreeTableVo);
        }
        return this.buildTreeData(menuTreeTableVoList, parentId);
    }

    @Override
    public List<Tree<Integer>> getTreeList(String clientId, Integer parentId, Boolean hasAccess) {
        List<Menu> menuList = getTreeMenuList(clientId, parentId, hasAccess);
        List<MenuTreeTableVo> menuTreeTableVoList = Lists.newArrayList();
        MenuTreeTableVo menuTreeTableVo;
        for (Menu menu: menuList) {
            menuTreeTableVo = new MenuTreeTableVo();
            CopyUtil.copyProperties(menu, menuTreeTableVo);
            menuTreeTableVo.setName(menu.getMenuName());
            menuTreeTableVoList.add(menuTreeTableVo);
        }
        return this.buildTreeData(menuTreeTableVoList, parentId);
    }

    @Override
    public List<Tree<Integer>> getTreeTable(MenuTreeTableParams menuTreeTableParams) {
        List<Menu> allMenuList = menuMapper.selectList(Wrappers.query());
        List<Menu> menuList = Lists.newCopyOnWriteArrayList(allMenuList);
        if(!Strings.isNullOrEmpty(menuTreeTableParams.getClientId())){
            List<Integer> appMenuIdList = permitAppMenuSpi.getMenuIdListByClientId(menuTreeTableParams.getClientId());
            menuList = getAppMenuList(allMenuList, appMenuIdList);
        }
        if(CollectionUtils.isEmpty(menuList)){
            return Lists.newArrayList();
        }
        Integer parentId = 1;
        if(Objects.nonNull(menuTreeTableParams.getParentId())){
            parentId = menuTreeTableParams.getParentId();
        }
        List<Integer> menuIdList = menuList.stream()
                .map(Menu::getId).distinct().collect(Collectors.toList());
        List<MenuTreeTableVo> menuTreeTableVoList = Lists.newArrayList();
        MenuTreeTableVo menuTreeTableVo;
        if(Objects.nonNull(menuTreeTableParams.getHasAccess()) && menuTreeTableParams.getHasAccess()){
            List<Permit> permitList = permitMapper.selectList(
                    new LambdaQueryWrapper<Permit>().in(Permit::getMenuId, menuIdList));
            for (Permit permit: permitList) {
                if(!Objects.isNull(permit.getParentId())){
                    menuTreeTableVo = new MenuTreeTableVo();
                    menuTreeTableVo.setId(RandomUtil.randomInt(1000, 10000) + RandomUtil.randomInt(1000, 10000));
                    menuTreeTableVo.setName(permit.getText());
                    menuTreeTableVo.setCode(String.valueOf(permit.getId()));
                    menuTreeTableVo.setParentId(permit.getMenuId());
                    menuTreeTableVo.setIcon(StringPool.DASH);
                    menuTreeTableVo.setRouter(StringPool.DASH);
                    menuTreeTableVo.setAppId(permit.getAppId());
                    menuTreeTableVo.setBusinessType(permit.getBusinessType());
                    menuTreeTableVo.setSequence(permit.getSequence());
                    menuTreeTableVo.setSign(permit.getSign());
                    menuTreeTableVo.setDeleted(permit.getDeleted());
                    menuTreeTableVo.setMenuType(MenuTypes.PERMIT.getValue());
                    menuTreeTableVo.setDescription(permit.getDescription());
                    menuTreeTableVoList.add(menuTreeTableVo);
                }
            }
        }
        for (Menu menu: menuList) {
            menuTreeTableVo = new MenuTreeTableVo();
            menuTreeTableVo.setId(menu.getId());
            menuTreeTableVo.setName(menu.getMenuName());
            menuTreeTableVo.setCode(menu.getMenuCode());
            menuTreeTableVo.setParentId(menu.getParentId());
            menuTreeTableVo.setRouter(menu.getRouter());
            menuTreeTableVo.setKeepAlive(menu.getKeepAlive());
            menuTreeTableVo.setIcon(menu.getIcon());
            menuTreeTableVo.setLevel(menu.getLevel());
            menuTreeTableVo.setAppId(menu.getAppId());
            menuTreeTableVo.setAccessState(menu.getAccessState());
            menuTreeTableVo.setBusinessType(menu.getBusinessType());
            menuTreeTableVo.setSequence(menu.getSequence());
            menuTreeTableVo.setSign(menu.getSign());
            menuTreeTableVo.setDeleted(menu.getDeleted());
            menuTreeTableVo.setMenuType(MenuTypes.MENU.getValue());
            if(menu.getLevel() == 1){
                menuTreeTableVo.setMenuType(MenuTypes.MODULE.getValue());
            }
            if(menu.getLevel() == 0){
                menuTreeTableVo.setMenuType(MenuTypes.ROOT.getValue());
            }
            menuTreeTableVo.setDescription(menu.getDescription());
            menuTreeTableVoList.add(menuTreeTableVo);
        }
        return this.buildTreeData(menuTreeTableVoList, parentId);
    }

    /**
     * 构建treeData
     * @param menuTreeTableVoList 菜单树表格集合
     * @param rootId 根id
     * @return List<Tree<Integer>>
     */
    private List<Tree<Integer>> buildTreeData(List<MenuTreeTableVo> menuTreeTableVoList, Integer rootId){
        TreeNodeConfig treeNodeConfig = new TreeNodeConfig();
        return TreeUtil.build(menuTreeTableVoList, rootId, treeNodeConfig, (treeNode, tree) -> {
            tree.setId(treeNode.getId());
            tree.setParentId(treeNode.getParentId());
            tree.setWeight(treeNode.getSequence());
            tree.setName(treeNode.getName());
            tree.putExtra("menuName", treeNode.getName());
            tree.putExtra("code", treeNode.getCode());
            tree.putExtra("router", treeNode.getRouter());
            tree.putExtra("keepAlive", treeNode.getKeepAlive());
            tree.putExtra("icon", treeNode.getIcon());
            tree.putExtra("level",treeNode.getLevel());
            tree.putExtra("sign",treeNode.getSign());
            tree.putExtra("deleted", treeNode.getDeleted());
            tree.putExtra("appId", treeNode.getAppId());
            tree.putExtra("deletedDesc", DeletedEnum.getDesc(treeNode.getDeleted()));
            tree.putExtra("accessState", treeNode.getAccessState());
            tree.putExtra("accessStateDesc", AccessStates.getDesc(treeNode.getAccessState()));
            tree.putExtra("keepAliveDesc", KeepAliveTypes.getDesc(treeNode.getKeepAlive()));
            tree.putExtra("description", treeNode.getDescription());
            tree.putExtra("menuType", treeNode.getMenuType());
            tree.putExtra("menuTypeDesc", treeNode.getMenuTypeDesc());
            tree.putExtra("businessType", treeNode.getBusinessType());
            tree.putExtra("sequence", treeNode.getSequence());
        });
    }

    /**
     * 根据应用菜单Id列表获取菜单
     * @param allMenuList 所有菜单列表
     * @param appMenuIdList 菜单id列表
     * @return List<Menu>
     */
    private List<Menu> getAppMenuList(List<Menu> allMenuList, List<Integer> appMenuIdList){
        List<Menu> appMenuList = menuMapper.selectBatchIds(appMenuIdList);
        List<Integer> parentIdList;
        List<Menu> parentMenuList = Lists.newLinkedList(appMenuList);
        do {
            parentIdList = parentMenuList.stream().map(Menu::getParentId).distinct().collect(Collectors.toList());
            final List<Integer> menuIdList = Lists.newArrayList(parentIdList);
            parentMenuList = allMenuList.stream()
                    .filter(menu -> menuIdList.contains(menu.getId()))
                    .collect(Collectors.toList());
            appMenuList.addAll(parentMenuList);
        } while (CollectionUtil.isNotEmpty(parentIdList));
        return appMenuList.stream()
                .collect(Collectors.collectingAndThen(Collectors.toCollection(() ->
                        new TreeSet<>(Comparator.comparing(Menu::getId))), ArrayList::new));
    }
}
